from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.utils import timezone
from django.views import View
from django_redis import get_redis_connection
from decimal import Decimal
import json
from django import http
# from time import timezone
from django.utils import timezone
from django.db import transaction

# from meiduo_01.utils.views import LoginRequiredJsonMixin
from users.models import Address
from goods.models import SKU
from .models import OrderInfo, OrderGoods
from meiduo_01.utils.response_code import RETCODE
# Create your views here.


class OrderSuccessView(LoginRequiredMixin, View):
    def get(self, req):
        """返回提交订单成功页面"""
        # order_id=20191128201033000000009&payment_amount=7010.00&pay_method=2
        order_id = req.GET.get('order_id')
        payment_amount = req.GET.get('payment_amount')
        pay_method = req.GET.get('pay_method')

        context = {
            'order_id': order_id,
            'payment_amount': payment_amount,
            'pay_method': pay_method
        }

        return render(req, 'order_success.html', context)


class OrderCommitView(LoginRequiredMixin, View):
    """保存订单信息"""
    def post(self, req):
        """保存订单基本信息和商品信息"""
        # 接收参数
        json_dict = json.loads(req.body.decode())
        address_id = json_dict.get('address_id')
        pay_method = json_dict.get('pay_method')

        # 校验参数
        # 先要校验参数是否齐全
        if not all([address_id, pay_method]):
            return http.HttpResponseForbidden('缺少必传参数')
        # 校验地址
        try:
            address = Address.objects.get(id=address_id)
        except Address.DoesNotExist:
            return http.HttpResponseForbidden('Address参数错误')
        # 校验付款方式（PAY_METHODS_ENUM 这个方式：避免出现魔法数字）
        if pay_method not in ([OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]):
            return http.HttpResponseForbidden('pay_method参数错误')

        # with之后的的代码会在事务中执行（为了完整性 把以下代码全包括）
        with transaction.atomic():
            # 在数据库操作之前需要制定保存点（保存数据库最初的状态）
            save_id = transaction.savepoint()
            # 暴力回滚
            try:
                user = req.user

                # 获取订单编号：时间+user_id == '201910011334000000001'  年月日时分秒
                order_id = timezone.localtime().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
                # 保存订单基本信息(一的一方)(先要去上面获取用户和订单编号)
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0),
                    freight=Decimal(10.00),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method==OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else OrderInfo.ORDER_STATUS_ENUM['UNSEND']
                )

                # 保存订单商品信息(多)
                # 查询redis购物车中，被勾选的商品
                redis_conn = get_redis_connection('carts')
                # 获取购物车中的所有勾选和未勾选的商品
                redis_cart = redis_conn.hgetall('carts_%s' % user.id)
                # 获取被勾选的商品的sku_id
                redis_selected = redis_conn.smembers('selected_%s' % user.id)
                # 构造购物车中被勾选商品的数据
                new_cart_dict = {}
                for sku_id in redis_selected:
                    # new_cart_dict中的sku_id = 商品数量  (取出来是bytes类型，int一下)
                    new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])

                # 获取被勾选的商品的sku_id
                sku_ids = new_cart_dict.keys()
                for sku_id in sku_ids:
                    # 读取购物车商品信息
                    # 要现场查，不能出现缓存，所以不能用filter(id__in=sku_ids)

                    # 放在了for sku_id in sku_ids:里面，表示：针对每一样商品进行多次循环，而不是针对整个购物车
                    # 每个商品都有多次下单的机会，直到库存不足
                    while True:

                        sku = SKU.objects.get(id=sku_id)

                        # 获取原始的库存和销量
                        origin_stock = sku.stock
                        origin_sales = sku.sales

                        # sku_count = new_cart_dict[sku.id]  # 老师的写法
                        sku_count = new_cart_dict.get(sku.id)
                        # print(sku_count,"=======")
                        # 判断订单中的商品数据，是否大于库存，如果大于，响应库存不足
                        if sku_count > sku.stock:
                            # 库存不足，回滚，（知道明显的错，在特定的地方加回滚）
                            transaction.savepoint_rollback(save_id)
                            return http.JsonResponse({'code': RETCODE.STOCKERR, 'errmsg': '库存不足'})

                        # # 递交订单的核心 SKU 减库存，加销量
                        # sku.stock -= sku_count
                        # sku.sales += sku_count
                        # # 记得保存啊！！！
                        # sku.save()
                        # # sku.save(update_fields=["stock",'sales'])  只更新["stock",'sales']这两个字段

                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        result = SKU.objects.filter(id=sku_id, stock=origin_stock).update(stock=new_stock, sales=new_sales)
                        # 如果在更新数据时，原始数据变化了，返回0，表示有资源抢夺
                        if result == 0:
                            # 直接返回下单失败，还买不买东西了
                            # return http.JsonResponse('下单失败')
                            continue

                        # SPU 加销量
                        sku.spu.sales += sku_count
                        sku.spu.save()

                        OrderGoods.objects.create(
                            order=order,
                            sku=sku,
                            count=sku_count,
                            price=sku.price
                        )
                        # 上面OrderInfo新创建的纪录还差total_count，total_amount
                        # 累加订单商品的数量和总价到订单基本信息表
                        order.total_count += sku_count
                        order.total_amount += sku_count * sku.price

                        # 下单成功，记得break
                        break

                # 再加运费
                order.total_amount += order.freight
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_id)
                return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '下单失败'})

            # 数据库操作成功，明显的提交一次事务
            transaction.savepoint_commit(save_id)

        # 返回响应(其余的都是后端自己的操作，前端只需要order_id就可)
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'order_id': order_id})
        pass


class OrderSettlementView(LoginRequiredMixin, View):
    """结算订单"""
    def get(self, req):
        """获取结算订单的页面"""
        # 获取用户
        user = req.user

        # 查询用户地址
        try:
            # Address.objects.filter(id=user.id)
            addresses = Address.objects.filter(user=user, is_deleted=False)
        except Exception as e:
            # return
            # 如果没有查询出收货地址，返回None，可以去编辑收货地址
            addresses = None

        # 查询购物车中勾选了的商品
        redis_conn = get_redis_connection('carts')
        # 获取购物车中的所有勾选和未勾选的商品
        redis_cart = redis_conn.hgetall('carts_%s' % user.id)
        # 获取被勾选的商品的sku_id
        redis_selected = redis_conn.smembers('selected_%s' % user.id)
        # 构造购物车中被勾选商品的数据
        new_cart_dict = {}
        for sku_id in redis_selected:
            # new_cart_dict[int(sku_id)] = redis_cart['sku_id']['count']
            # 把从hash里取出来的该sku_id对应的数量，给了新字典里的这个sku_id
            new_cart_dict[int(sku_id)] = int(redis_cart[sku_id])  # TODO 【数据格式】还是有点搞不懂，为啥要加int？

        # 遍历new_cart_dict，取出其中的sku_id和count
        # 获取被勾选商品的sku_id
        sku_ids = new_cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)
        total_count = 0
        total_amount = Decimal(0)

        # 遍历skus 给每个sku_id 补充count和小计amount
        for sku in skus:
            sku.count = new_cart_dict[sku.id]
            sku.amount = sku.price * sku.count

            total_count += sku.count
            total_amount += sku.amount

        # 指定默认的邮费
        freight = Decimal(10.00)
        payment_amount = total_amount + freight

        context = {
            'addresses': addresses,
            'skus': skus,
            'total_amount': total_amount,
            'total_count': total_count,
            'freight': freight,
            'payment_amount': payment_amount
        }
        return render(req, 'place_order.html', context)
